<?php
/**
 * Created by PhpStorm.
 * User: yuanxr
 * Date: 2018/6/10
 * Time: 上午8:32
 */

namespace Utility\Data;
use \mysqli;
use Utility\Extend\EArray;
use Utility\Extend\EString;
use \Exception;
/**********************************************************************************************************************
 * /asql：使用mysqli操作mysql（OLAP）。主要用于阿里云分析型数据库
 **********************************************************************************************************************/
class Asql
{
    //设置实例化数组类和字符串类
    private $_EArray;
    private $_EString;

    function __construct(){
        $this->_EArray = new EArray();
        $this->_EString = new EString();
    }
    
    /**
     * query
     *
     * @param array $db 数据库连接配置数组
     * @param mixed $sqls
     * @return array 读取到数据，则返回数据数组。否则返回null
     */
    public function query(array $db, $sqls, $params = null)
    {
        $mysqli = $this->_mysqli($db);
        $sqls = $this->_preset($sqls, $params);
        
        // 如果sqls是字符串
        if (is_string($sqls)) {
            $res = $mysqli->query($sqls);
            if (!$res) {
                try{
                    throw new Exception($mysqli->error);
                }catch(Exception $e){
                    return  $e->getMessage();
                }
            }
            return $res->fetch_all(MYSQLI_ASSOC);
        }

        // 如果sqls是数组
        $result = [];
        foreach ($sqls as $v) {
            $res = $mysqli->query($v);
            if (!$res) {
                try{
                    throw new Exception($mysqli->error);
                }catch(Exception $e){
                    return  $e->getMessage();
                }
            }
            $result[] = $res->fetch_all(MYSQLI_ASSOC);
        }
        return $result;
    }

    /**
     * write
     *
     * @param array $db 数据库连接配置数组
     * @param mixed $sqls sql语句，不管插入的条数是多少，只写一句
     * @param array $params 查询mysql时与sql语句配合的参数
     * @return boolean 成功返回true，失败返回false
     */
    public function write(array $db, $sqls, $params = null)
    {
        $mysqli = $this->_mysqli($db);
        $sqls = $this->_preset($sqls, $params);
        
        // 如果sql是字符串
        if (is_string($sqls)) {
            if (!$mysqli->query($sqls)) {
                try{
                    throw new Exception($mysqli->error);
                }catch(Exception $e){
                    return  $e->getMessage();
                }
            }
            return;
        }

        // 如果sqls是数组
        foreach ($sqls as $v) {
            if (!$mysqli->query($v)) {
                try{
                    throw new Exception($mysqli->error);
                }catch(Exception $e){
                    return  $e->getMessage();
                }
            }
        }
    }
  
    /**
     * _preset
     *
     * @param mixed $sqls sql源字符串或数组
     * @param array $params 参数数组
     * @return array 组合好的sqls字符串数组
     */
    public function _preset($sqls, $params)
    {

    // 如果params == null，则直接返回sqls
        if ($params == null) {
            return $sqls;
        }

        // 如果sqls是单条语句，则直接返回_assemble
        // 注意，有时候会出现一维数组中仅带有一个sql语句的情况，如果是这样，则降维为字符串
        if (is_string($sqls) || (is_array($sqls) && count($sqls) == 1)) {
            $sqls = is_array($sqls) ? $sqls[0] : $sqls;
            return $this->_assemble($sqls, $params);
        }

        // 声明需要返回的值
        $result = [];

        // 遍历进行组合，取组合后的值
        for ($i=0; $i<count($sqls); $i++) {
            $result[] = $this->_assemble($sqls[$i], $params[$i]);
        }

        // 返回
        return $result;
    }

    /**
     * _assemble
     *
     * @param string $sql 带有参数的源sql语句
     * @param array $param 替换为sql实际参数的参数数组
     * @return string 组合好的sql单条语句
     */
    public function _assemble(string $sql, array $param)
    {
    
    // 需要对insert语句进行特殊处理，所以，需要进行一下判断
        if ($this->_EString->has($sql, 'insert into')) {
            return $this->_assemble_insert($sql, $param);
        }

        // 将sql使用?分解为数组，其count永远等于?数量+1
        $split = $this->_EString->split($sql, '?');

        // 遍历
        for ($i=0, $count=count($split)-1; $i<$count; $i++) {
            $tmp = gettype($param[$i]) == 'string' ? '"' . $param[$i] . '"' : $param[$i];
            $split[$i] .= $tmp;
        }

        // 返回
        return $this->_EString->join($split, '');
    }

    /**
     * _assemble_insert
     */
    private function _assemble_insert(string $sql, array $param)
    {
    // 将sql分为左右两部分，将左半部分直接赋值给需要返回的result
        $result = $this->_EString->left($sql, 'values') . 'values';
        $right = $this->_EString->right($sql, 'values');

        // 根据param的数量，生成",('', '')"语句：（注意，要将字符串型和其它型变量分开）

        // 数组深度为1的时候
        if ($this->_EArray->depth($param) == 1) {
            $tmp = '(';
            for ($i=0, $l=count($param); $i<$l; $i++) {
                $tmp .= (gettype($param[$i]) == 'string' ? '"' . $param[$i] . '"' : $param[$i]) . ',';
            }
            $result .= rtrim($tmp, ',') . ')';
            return $result;
        }
    
        // 数组深度大于1的时候
        foreach ($param as $v) {
            $tmp = '(';
            for ($i=0, $l=count($v); $i<$l; $i++) {
                $tmp .= (gettype($v[$i]) == 'string' ? '"' . $v[$i] . '"' : $v[$i]) . ',';
            }
            $result .= rtrim($tmp, ',') . '),';
        }
        return rtrim($result, ',');
    }

    /**
     * _mysqli
     */
    private function _mysqli(array $db)
    {
        $mysqli = new mysqli($db['host'], $db['user'], $db['pwd'], $db['database'], $db['port']);
        if ($mysqli->connect_error) {
            try{
                throw new Exception('ASQL连接分析型数据库出错！错误码：' . $mysqli->connect_errno . '，错误信息：' . $mysqli->connect_error);
            }catch(Exception $e){
                return  $e->getMessage();
            }
        }
        $mysqli->query('set names utf8');
        return $mysqli;
    }

    /**
     * writeSyn
     *_mysqli的可回滚可同步的writeSyn
     */
    public function writeSyn(array $db, $sqls, $params = null, $function_sync=null, $data=null){
        $mysqli = $this->_mysqli($db);
        $sqls = $this->_preset($sqls, $params);
        //关闭自动提交事务
        $mysqli->autocommit(FALSE);
        try{
            // 如果sql是字符串
            if (is_string($sqls)) {
                if (!$mysqli->query($sqls)) {
                    throw new Exception($mysqli->error);
                }
            }elseif(is_array($sqls)){
                // 如果sqls是数组
                foreach ($sqls as $v) {
                    if (!$mysqli->query($v)) {
                        throw new Exception($mysqli->error);
                    }
                }
            }
            $mysqli->commit();
            $mysqli->autocommit(TRUE);
            if($function_sync){
                if(!$function_sync($data)){
                    throw new Exception('write，注入方法执行错误');
                }
            }
        }catch(Exception $e){
            $mysqli->rollback();
            echo $e->getMessage();exit();
        }
    }

    /**
     * lastidSyn
     * 执行语句并且返回最后一个id并且执行同步或者其他的操作
     */
    public function lastidSyn(array $db, string $sql, $params = null, $function_sync=null, $data=null){
        $mysqli = $this->_mysqli($db);
        $sql = $this->_preset($sql, $params);
        //关闭自动提交事务
        $mysqli->autocommit(FALSE);
        try{
            if (!$mysqli->query($sql)) {
                throw new Exception($mysqli->error);
            }
            $result_id  = mysqli_insert_id($mysqli);
            $mysqli->commit();
            $mysqli->autocommit(TRUE);
            if($function_sync){
                if(!$function_sync($data)){
                    throw new Exception('lastid，注入方法执行错误');
                }
            }
        }catch(Exception $e){
            $mysqli->rollback();
            echo $e->getMessage();exit();
        }
        return $result_id;
    }

}
